//
//  ViewController.m
//  消息转发
//
//  Created by Hello Cai on 2019/11/4.
//  Copyright © 2019 Hello Cai. All rights reserved.
//

#import "ViewController.h"
#import "Person.h"
#import <objc/message.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // 执行 fun 函数. 如果self存在fun方法，则消息不会被转发，做后面的操作
//    [self performSelector:@selector(fun)];

    [self performSelector:@selector(interest:) withObject:@"张三"];
}

//【案例一】
//==========第一步消息动态解析处理掉了============
// 重写 resolveInstanceMethod: 添加对象方法实现
//+ (BOOL)resolveInstanceMethod:(SEL)sel {
//    if (sel == @selector(fun)) { // 如果是执行 fun 函数，就动态解析，指定新的 IMP
//        /*
//         "v@:"含义：v表示void，@表示An object（typed id），冒号表示SEL。
//         以上含义获取方式：按住option+点击class_addMethod，在弹出的文档中找到types，
//         点击其链接即可在网页中找到各符号的含义
//         */
//        class_addMethod([self class], sel, (IMP)funMethod, "v@:");
//        return YES;
//    }
//    return [super resolveInstanceMethod:sel];
//}
////默认有两个参数
//void funMethod(id obj, SEL _cmd) {
//    NSLog(@"funMethod");
//}


//【案例二】
//==========第一步没函数实现，由第二步消息接受者重定向处理============
//+ (BOOL)resolveInstanceMethod:(SEL)sel {
//    return YES; // 为了进行下一步 消息接受者重定向
//}
//// 消息接受者重定向
//- (id)forwardingTargetForSelector:(SEL)aSelector {
//    if (aSelector == @selector(fun)) {
//        return [[Person alloc] init];
//        // 返回 Person 对象，让 Person 对象接收这个消息
//    }
//    return [super forwardingTargetForSelector:aSelector];
//}

//【案例三】
//==========第一步和第二步没处理，由第三步消息重定向处理============
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    //无论是返回YES还是返回NO，只要没有添加其他函数的实现，就会进入下一步：消息接受者重定向
    return YES; // 为了进行下一步 消息接受者重定向
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    /*若返回的对象不是nil，也不是self，则由该对象来处理消息。
      若返回nil或self，就进入下一步：消息重定向
     */
    return nil; // 为了进行下一步 消息重定向
}

// 获取函数的参数和返回值类型，返回签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    /*如果能返回一个NSMethodSignature对象（函数签名），Runtime 系统就会创建一个 NSInvocation 对象，并通过 forwardInvocation: 消息通知当前对象，给予此次消息发送最后一次寻找 IMP 的机会。
      如果返回nil，则 Runtime 系统会直接发出 doesNotRecognizeSelector: 消息，程序也就崩溃了。
    */
    if ([NSStringFromSelector(aSelector) isEqualToString:@"fun"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }else if ([NSStringFromSelector(aSelector) isEqualToString:@"interest:"]){
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

// 消息重定向
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = anInvocation.selector;   // 从 anInvocation 中获取消息

    Person *p = [[Person alloc] init];
    
    if([p respondsToSelector:sel]) {   // 判断 Person 对象方法是否可以响应 sel
        [anInvocation invokeWithTarget:p];  // 若可以响应，则将消息转发给其他对象处理
    } else {
        [self doesNotRecognizeSelector:sel];  // 若仍然无法响应，则报错：找不到响应方法
    }
}

@end
